﻿using Heroius.Extension;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Media3D;

namespace IEPI.BIATranscribe
{
    //ref: http://www.charlespetzold.com/blog/2007/08/250638.html
    //ref: http://stackoverflow.com/questions/38524712/stretch-quadrilateral-to-fit-rectangle

    /// <summary>
    /// 展示可定位文档页
    /// </summary>
    public partial class PageView : UserControl
    {
        /// <summary>
        /// 展示可定位文档页
        /// </summary>
        public PageView()
        {
            InitializeComponent();

            mesh.Positions = new Point3DCollection(new []{
                new Point3D(0,0,0),
                new Point3D(0,Properties.Settings.Default.PageHeight,0),
                new Point3D(Properties.Settings.Default.PageWidth,0,0),
                new Point3D(Properties.Settings.Default.PageWidth, Properties.Settings.Default.PageHeight, 0)
            });
            mesh.TextureCoordinates = new PointCollection(new[] {
                new Point(0, Properties.Settings.Default.PageHeight),
                new Point(0,0),
                new Point(Properties.Settings.Default.PageWidth, Properties.Settings.Default.PageHeight),
                new Point(Properties.Settings.Default.PageWidth, 0)
            });
            Cam.Position = new Point3D(Properties.Settings.Default.PageWidth / 2, Properties.Settings.Default.PageHeight / 2, Properties.Settings.Default.CamDist);
            Cam.Width = Properties.Settings.Default.CamWidth;
        }
        
        /// <summary>
        /// 页面定位属性，4个坐标点依照 左上，左下，右上，右下 顺序以逗号分隔，横纵坐标以空格分隔
        /// </summary>
        public string Locator
        {
            get { return (string)GetValue(LocatorProperty); }
            set { SetValue(LocatorProperty, value); }
        }

        /// <summary>
        /// 页面定位属性，4个坐标点依照 左上，左下，右上，右下 顺序以逗号分隔，横纵坐标以空格分隔
        /// </summary>
        public static readonly DependencyProperty LocatorProperty =
            DependencyProperty.Register("Locator", typeof(string), typeof(PageView), new FrameworkPropertyMetadata("", new PropertyChangedCallback(SetPageViewDocLocator)));
        /// <summary>
        /// 当Locator变化时，更新界面
        /// </summary>
        /// <param name="d"></param>
        /// <param name="e"></param>
        private static void SetPageViewDocLocator(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (string.IsNullOrWhiteSpace(e.NewValue.ToString())) return;
            var points = e.NewValue.ToString().Split(',').Select(pstr => { var pc = pstr.Split(' '); return new Point3D(pc[0].As<double>(), pc[1].As<double>(), 0); }).ToArray();
            (d as PageView).SetCorner(points);
        }

        /// <summary>
        /// 顺序与XAML定义一致：LB,LT,RB,RT
        /// </summary>
        /// <param name="CornerPoints"></param>
        void SetCorner(Point3D[] CornerPoints)
        {
            for (int i = 0; i < 4; i++)
            {
                mesh.Positions[i] = CornerPoints[i];
            }
        }

        bool positionRequest = false;
        public void BeginRequest() { positionRequest = true; PositionRequestChanged?.Invoke(true); }
        public void EndRequest() { positionRequest = false; PositionRequestChanged?.Invoke(false); }
        
        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonDown(e);
            if (positionRequest)
            {
                var pt = Simple2Dto3D(viewport3d, e.GetPosition(viewport3d));
                PointPopping?.Invoke(this, pt);
            }
        }

        protected override void OnMouseRightButtonDown(MouseButtonEventArgs e)
        {
            base.OnMouseRightButtonDown(e);
            if (positionRequest) EndRequest();
        }

        public event Action<PageView, Point3D> PointPopping;

        public event Action<bool> PositionRequestChanged;

        #region Petzold Methods

        // The following two methods only work with OrthographicCamera,
        // with LookDirection of (0, 0, -1) and UpDirection of (0, 1, 0).
        // More advanced conversion routines can be found in the 
        // Petzold.Media3D library.

        // Converts a 2D point in device-independent coordinates relative 
        //  to Viewport3D to 3D space.
        Point3D Simple2Dto3D(Viewport3D vp, Point pt)
        {
            OrthographicCamera cam = CheckRestrictions(vp);
            double scale = cam.Width / vp.ActualWidth;
            double x = scale * (pt.X - vp.ActualWidth / 2) + cam.Position.X;
            double y = scale * (vp.ActualHeight / 2 - pt.Y) + cam.Position.Y;

            return new Point3D(x, y, 0);
        }

        // Converts a 3D point to 2D in device-independent coordinates
        //  relative to Viewport3D.
        Point Simple3Dto2D(Viewport3D vp, Point3D point)
        {
            OrthographicCamera cam = CheckRestrictions(vp);
            double scale = vp.ActualWidth / cam.Width;
            double x = vp.ActualWidth / 2 + scale * (point.X - cam.Position.X);
            double y = vp.ActualHeight / 2 - scale * (point.Y - cam.Position.Y);
            return new Point(x, y);
        }

        OrthographicCamera CheckRestrictions(Viewport3D vp)
        {
            OrthographicCamera cam = vp.Camera as OrthographicCamera;

            if (cam == null)
                throw new ArgumentException("Camera must be OrthographicCamera");

            if (cam.LookDirection != new Vector3D(0, 0, -1))
                throw new ArgumentException("Camera LookDirection must be (0, 0, -1)");

            if (cam.UpDirection != new Vector3D(0, 1, 0))
                throw new ArgumentException("Camera UpDirection must be (0, 1, 0)");

            return cam;
        }

        #endregion
        
    }
}
